﻿using System.Globalization;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;

namespace Devonline.Core;

public static class StringExtensions
{
    /// <summary>
    /// 获取类型 TValue 的转换值, 或者默认值
    /// </summary>
    /// <returns></returns>
    public static TValue? GetValueOrDefault<TValue>(this string value) where TValue : IConvertible => string.IsNullOrWhiteSpace(value) ? default : value.To<TValue>();
    /// <summary>
    /// json 字符串转对象, 泛型用法
    /// </summary>
    /// <typeparam name="T">需要反序列化的对象类型</typeparam>
    /// <param name="value">需要反序列化的字符串</param>
    /// <param name="settings">反序列化设置</param>
    /// <returns></returns>
    public static T? ToJsonObject<T>(this string value, JsonSerializerSettings? settings = default) => JsonConvert.DeserializeObject<T>(value, settings ?? AppSettings.JsonSerializerSettings);

    /// <summary>
    /// 字符串是否为空或 null
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool IsNullOrEmpty(this string value) => string.IsNullOrEmpty(value);
    /// <summary>
    /// 字符串是否不为空或 null
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static bool IsNotNullOrEmpty(this string value) => !string.IsNullOrEmpty(value);
    /// <summary>
    /// 判断字符串是否无值, 既不为空也不为空格
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool IsNullOrWhiteSpace(this string value) => string.IsNullOrWhiteSpace(value);
    /// <summary>
    /// 判断字符串是否有值, 既不为空也不为空格
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool IsNotNullOrWhiteSpace(this string value) => !string.IsNullOrWhiteSpace(value);

    /// <summary>
    /// 获取文件的绝对路径
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static string GetAbsolutePath(this string path) => Path.GetPathRoot(path) == null ? path : Path.Combine(AppSettings.StartupPath, path);

    /// <summary>
    /// 对字符串值按 THashAlgorithm 算法计算 Hash Code
    /// </summary>
    /// <typeparam name="THashAlgorithm">Hash 算法</typeparam>
    /// <param name="hashAlgorithm">hash 算法实例</param>
    /// <param name="value">待计算值</param>
    /// <param name="encoding">编码方式</param>
    /// <returns></returns>
    public static byte[] GetHashValue<THashAlgorithm>(this THashAlgorithm hashAlgorithm, string value, Encoding? encoding = default) where THashAlgorithm : System.Security.Cryptography.HashAlgorithm => hashAlgorithm.ComputeHash((encoding ?? Encoding.UTF8).GetBytes(value));
    /// <summary>
    /// 对字符串值按 THashAlgorithm 算法计算 Hash Code, 并使用 Base64 进行编码后返回
    /// </summary>
    /// <typeparam name="THashAlgorithm">Hash 算法</typeparam>
    /// <param name="hashAlgorithm">hash 算法实例</param>
    /// <param name="value">待计算值</param>
    /// <param name="encoding">编码方式</param>
    /// <returns></returns>
    public static string GetHashString<THashAlgorithm>(this THashAlgorithm hashAlgorithm, string value, Encoding? encoding = default) where THashAlgorithm : System.Security.Cryptography.HashAlgorithm => Convert.ToBase64String(hashAlgorithm.GetHashValue(value, encoding));
    /// <summary>
    /// 获取字符串的 SHA256 Hash 值, 这是一种直接使用 SHA256 的更高效的方式
    /// </summary>
    /// <param name="value">原始字符串</param>
    /// <returns></returns>
    public static byte[] GetHashValue(this string value, Encoding? encoding = default) => SHA256.Create().ComputeHash((encoding ?? Encoding.UTF8).GetBytes(value));
    /// <summary>
    /// 获取字符串的 SHA256 Hash 值的 Base64 字符串表示形式, 这是一种直接使用 SHA256 的更高效的方式
    /// </summary>
    /// <param name="value">原始字符串</param>
    /// <returns></returns>
    public static string GetHashString(this string value, Encoding? encoding = default) => Convert.ToBase64String(value.GetHashValue(encoding));

    /// <summary>
    /// 首字母转小写
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string FirstCharToLower(this string value)
    {
        if (value.IsNotNullOrEmpty() && char.IsUpper(value[AppSettings.UNIT_ZERO]))
        {
            return char.ToLower(value[AppSettings.UNIT_ZERO], CultureInfo.CurrentCulture) + value[1..];
        }

        return value;
    }
    /// <summary>
    /// 首字母转大写
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string FirstCharToUpper(this string value)
    {
        if (value.IsNotNullOrEmpty() && char.IsLower(value[AppSettings.UNIT_ZERO]))
        {
            return char.ToUpper(value[AppSettings.UNIT_ZERO], CultureInfo.CurrentCulture) + value[1..];
        }

        return value;
    }
    /// <summary>
    /// 首字母小写的 CamelCase 风格
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string ToCamelCase(this string value) => value.FirstCharToLower();

    /// <summary>
    /// 关键信息脱敏, 保留固定长度, 前面 3 位, 后面 4 位
    /// </summary>
    /// <param name="value">需要脱敏的关键信息</param>
    /// <returns></returns>
    public static string Desensitize(this string? value)
    {
        if (value is null)
        {
            return string.Empty;
        }

        if (value.Length - AppSettings.UNIT_THREE - AppSettings.UNIT_FOUR > 0)
        {
            return value.Desensitize(AppSettings.UNIT_THREE, AppSettings.UNIT_FOUR);
        }

        return value;
    }
    /// <summary>
    /// 关键信息脱敏
    /// </summary>
    /// <param name="value">需要脱敏的关键信息</param>
    /// <param name="startLength">起始保留长度</param>
    /// <param name="endLength">末尾保留长度</param>
    /// <returns></returns>
    public static string Desensitize(this string? value, int startLength = AppSettings.UNIT_THREE, int endLength = AppSettings.UNIT_FOUR)
    {
        if (value is null)
        {
            return string.Empty;
        }

        if (value.Length - startLength - endLength > 0)
        {
            return value[AppSettings.UNIT_ZERO..startLength].PadRight(value.Length - endLength, AppSettings.CHAR_STAR) + value[^endLength..];
        }

        return value;
    }

    /// <summary>
    /// 验证一个字符串是否包含另一个字符串的的任何截取内容
    /// </summary>
    /// <param name="value">第一个字符串</param>
    /// <param name="another">另一个字符串</param>
    /// <param name="separator">分隔符</param>
    /// <returns></returns>
    public static bool ContainsAny(this string value, string another, params char[]? separator)
    {
        var values = value.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)?.Select(x => x.ToCamelCase());
        var anothers = another.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)?.Select(x => x.ToCamelCase());
        if (values is null || anothers is null)
        {
            return false;
        }

        return anothers.Intersect(values)?.Any() ?? false;
    }
    /// <summary>
    /// 验证一个字符串是否包含另一个字符串的的任何截取内容
    /// </summary>
    /// <param name="value">第一个字符串</param>
    /// <param name="another">另一个字符串</param>
    /// <param name="separator">分隔符</param>
    /// <returns></returns>
    public static bool ContainsAny(this string value, string another, params string[]? separator)
    {
        var values = value.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)?.Select(x => x.ToCamelCase());
        var anothers = another.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)?.Select(x => x.ToCamelCase());
        if (values is null || anothers is null)
        {
            return false;
        }

        return anothers.Intersect(values)?.Any() ?? false;
    }
    /// <summary>
    /// 检测字符串中是否包含列表中的关键词
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <param name="keys">关键词列表</param>
    /// <param name="ignoreCase">忽略大小写</param>
    /// <returns></returns>
    public static bool Contains(this string value, IEnumerable<string> keys, bool ignoreCase = true)
    {
        if (!keys.Any() || string.IsNullOrEmpty(value))
        {
            return false;
        }

        if (ignoreCase)
        {
            return Regex.IsMatch(value, string.Join("|", keys), RegexOptions.IgnoreCase);
        }

        return Regex.IsMatch(value, string.Join("|", keys));

    }

    /// <summary>
    /// ping a host or ip address and then execute the action
    /// if ping success one times, will execute success action and return
    /// otherwise will execute fail action every times until success or maximum frequency
    /// </summary>
    /// <param name="host">host or ip address</param>
    /// <param name="frequency">the max number of ping times</param>
    /// <param name="success">execute when success</param>
    /// <param name="fail">execute when fail</param>
    /// <returns></returns>
    public static async Task<bool> PingAsync(this string host, int frequency = AppSettings.UNIT_FOUR, Action? success = default, Action? fail = default)
    {
        var ping = new Ping();
        var index = 1;
        var result = false;

        do
        {
            var pingReply = await ping.SendPingAsync(host, AppSettings.UNIT_TEN * AppSettings.UNIT_THOUSAND);
            if (pingReply.Status == IPStatus.Success)
            {
                result = true;
                success?.Invoke();
                break;
            }

            fail?.Invoke();
            Thread.Sleep(AppSettings.UNIT_THOUSAND);
        } while (!result && index++ <= frequency);

        return result;
    }
}